/**
 * 报表设计公司(2011)
 */
package com.reportgear.report.print;

import java.awt.print.PageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.swing.JOptionPane;

import net.abbp.core.util.StringUtils;
import net.abbp.core.utils.UnitUtils;
import net.abbp.report.model.Cell;
import net.abbp.report.model.ReportModel;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.model.auxiliary.RowType;
import com.reportgear.report.setting.PaperSetting;
import com.reportgear.report.setting.ReportSettings;

/**
 * 报表页面生成迭代器,重要的分页算法在这里
 * 
 * @version 1.0 2011-3-23
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since ANNCSR 2.0
 */
public class ReportPageIterator implements Iterator<ReportPage> {
    private static final Logger logger = LoggerFactory.getLogger(ReportPageIterator.class.getName());
    // 分页列索引
    private final List<Integer> pageColumnIndexList = new ArrayList<Integer>();
    // 分页行索引
    private final List<RowPageIndex> pageRowIndexList = new ArrayList<RowPageIndex>();
    // 总页数
    private int pageCount = 0;
    // 页面列表
    private LinkedList<ReportPage> pageList = new LinkedList<ReportPage>();

    private FT detailColumns;
    private FT detailRows;

    private Thread pageGenerateThread;

    // 处理的报表模型
    private ReportModel model;
    private int pageColumn = -1;

    /**
     * 构造函数
     * 
     * @param report 报表对象
     */
    public ReportPageIterator(ReportModel report) {
        this.model = report;

        dealWithPageInfo();
    }

    public ReportPageIterator(ReportModel report, int pageColumn) {
        this.model = report;
        this.pageColumn = pageColumn;

        dealWithPageInfo();
    }

    /**
     * 分组页信息
     */
    private static class GroupPageInfo {
        // 分组页码
        private int pageNumber;
        // 总页数(默认-1，不存在，由程序改写)
        private int totalPages = -1;

        private GroupPageInfo(int pageNumber) {
            this.pageNumber = pageNumber;
        }
    }

    /**
     * 页面生成线程
     */
    private class PageGenerateThread extends Thread {

        private PageGenerateThread() {}

        /**
         * 处理报表页
         * 
         * @param rowFT 行RowFT
         * @param columnFT 列FT
         * @param pageNum 页码
         * @param groupPageNum 分组页码
         * @param groupPageCount 分组总页数
         */
        private void generateReportPage(RowFT rowFT, FT columnFT, int pageNum, int groupPageNum, int groupPageCount) {
            ReportPage reportPage = new ReportPage(model, rowFT, columnFT);

            reportPage.setCurrentPageNumber(pageNum);
            reportPage.setTotalPages(pageCount);
            reportPage.setGroupPageNumber(groupPageNum);
            reportPage.setGroupTotalPages(groupPageCount);

            pageList.add(reportPage);
        }

        @Override
        public void run() {
            int pageNum = 1;
            int groupPageNum = 1;
            RowFT rowFT;
            FT columnFT;

            // 行信息
            RowPropList rpl = model.getRowPropList();
            // 存储分组页信息
            Map<String, GroupPageInfo> groupInfoMap = new HashMap<String, GroupPageInfo>();

            // 计算组分页信息
            for (int i = 0, rsize = pageRowIndexList.size() - 1; i < rsize; i++) {
                // 如果是分组,则行属性必定为分组
                if (rpl.isGroup(pageRowIndexList.get(i).getRow())) {
                    for (GroupPageInfo gpi : groupInfoMap.values()) {
                        if (gpi.totalPages == -1) {
                            gpi.totalPages = groupPageNum - 1;
                        }
                    }
                    groupPageNum = 1;
                }

                for (int j = 0, csize = pageColumnIndexList.size() - 1; j < csize; j++) {
                    groupInfoMap.put(i + "_" + j, new GroupPageInfo(groupPageNum));

                    groupPageNum++;
                }
            }

            for (GroupPageInfo gpi : groupInfoMap.values()) {
                if (gpi.totalPages == -1) {
                    gpi.totalPages = groupPageNum;
                }
            }

            for (int i = 0, rsize = pageRowIndexList.size() - 1; i < rsize; i++) {
                rowFT = new RowFT(pageRowIndexList.get(i), pageRowIndexList.get(i + 1));
                for (int j = 0, csize = pageColumnIndexList.size() - 1; j < csize; j++) {
                    columnFT = new FT(pageColumnIndexList.get(j), pageColumnIndexList.get(j + 1));

                    GroupPageInfo gpi = groupInfoMap.get(i + "_" + j);
                    generateReportPage(rowFT, columnFT, pageNum, gpi.pageNumber, gpi.totalPages);

                    pageNum++;
                }
            }
        }
    }

    public boolean hasNext() {
        if (pageGenerateThread == null) {
            pageGenerateThread = new PageGenerateThread();
            pageGenerateThread.setPriority(3);
            pageGenerateThread.start();
        }

        while (true) {
            if (pageList.size() > 0) {
                return true;
            } else {
                try {
                    Thread.sleep(500L);
                } catch (final InterruptedException ex) {
                    logger.warn(ex.getMessage());
                }
                if (pageList.size() == 0) {
                    break;
                }
            }
        }

        return false;
    }

    public ReportPage next() {
        if (pageList.size() == 0) {
            return null;
        }

        return pageList.removeFirst();
    }

    public void remove() {
        throw new UnsupportedOperationException("不可移除");
    }

    public int size() {
        return pageCount;
    }

    /**
     * 获取页面信息
     */
    private void dealWithPageInfo() {
        ReportSettings reportSettings = model.getReportSettings();
        PaperSetting paperSetting = reportSettings.getPaperSetting();
        PaperSize pageSize = paperSetting.getPaperSize();
        Margin margin = paperSetting.getMargin();
        double pageWdith = 0;
        double pageHeight = 0;

        if (paperSetting.getOrientation() == PageFormat.LANDSCAPE) {
            pageWdith = Math.max(pageSize.getWidth(), pageSize.getHeight());
            pageHeight = Math.min(pageSize.getWidth(), pageSize.getHeight());
        } else if (paperSetting.getOrientation() == PageFormat.PORTRAIT) {
            pageWdith = Math.min(pageSize.getWidth(), pageSize.getHeight());
            pageHeight = Math.max(pageSize.getWidth(), pageSize.getHeight());
        }

        double temp = (pageWdith - margin.getLeft() - margin.getRight());
        int pagePixelWidth = UnitUtils.inch2pixel(temp);

        temp =
                pageHeight
                        - margin.getTop()
                        - margin.getBottom()
                        - reportSettings.getHeaderHeight()
                        - reportSettings.getFooterHeight();
        int pagePixelHeight = UnitUtils.inch2pixel(temp);

        int columnCount = model.getColumnCount();
        int rowCount = model.getRowCount();

        detailRows = new FT(0, rowCount);
        detailColumns = new FT(0, columnCount);

        this.calcColumnPaging(pagePixelWidth, columnCount);

        if (this.pageColumn >= 0) {
            this.calcRowPaging(pagePixelHeight, rowCount, pageColumn);
        } else {
            this.calcRowPaging(pagePixelHeight, rowCount);
        }
        pageCount = (pageColumnIndexList.size() - 1) * (pageRowIndexList.size() - 1);
    }

    // 计算分页列位置信息
    private void calcColumnPaging(int pagePixelWidth, int columnCount) {
        ColumnPropList columnWidthList = this.model.getColumnPropList();
        pageColumnIndexList.add(0);

        int fixedWidth = this.getFixedWidth();
        int calTotalWidth = fixedWidth;

        // TODO 一列都不能打完的时候会出现错误
        if (calTotalWidth > pagePixelWidth) {
            JOptionPane.showMessageDialog(null, "固定列宽大于纸张宽度，不能预览与打印", "警告", JOptionPane.WARNING_MESSAGE);
            return;
        }
        int calWidth = 0;
        for (int c = detailColumns.getFrom(); c < detailColumns.getTo(); c++) {
            if (this.model.isFixedColumn(c)) {
                continue;
            }

            calTotalWidth += (calWidth = columnWidthList.getWidth(c));

            if ((calTotalWidth <= pagePixelWidth) && !columnWidthList.isGroup(c)) {
                continue;
            }

            pageColumnIndexList.add(c);
            calTotalWidth = fixedWidth + calWidth;
        }

        pageColumnIndexList.add(columnCount);
    }

    /**
     * 返回固定列宽度
     * 
     * @return 宽度
     */
    private int getFixedWidth() {
        int result = 0;
        ColumnPropList columnWidthList = this.model.getColumnPropList();

        for (int i : this.model.getFixedHeaderColumns()) {
            result += columnWidthList.getWidth(i);
        }

        for (int i : this.model.getFixedFooterColumns()) {
            result += columnWidthList.getWidth(i);
        }

        return result;
    }

    // 计算分页行位置
    private void calcRowPaging(int pagePixelHeight, int rowCount) {
        RowPropList rowHeightList = this.model.getRowPropList();
        pageRowIndexList.add(new RowPageIndex(0));

        int fixedHeight = this.getFixedHeight(this.model);
        int calTotalHeight = fixedHeight;

        int calHeight = 0;

        for (int r = detailRows.getFrom(); r < detailRows.getTo(); r++) {
            if (this.model.isFixedRow(r)) {
                continue;
            }

            calTotalHeight += (calHeight = rowHeightList.getHeight(r));

            // 增加按组织机构和成本中心分页
            if (!rowHeightList.isGroup(r) && (calTotalHeight <= pagePixelHeight)) {
                continue;
            }

            // TODO 支持子表换行
            if (isRowHasSubReport(r)) {
                ReportModel rowSubModel = this.getsRowSubReport(r);
                int rowSubFixedHeight = this.getFixedHeight(rowSubModel);

                calTotalHeight -= calHeight;
                calTotalHeight += rowSubFixedHeight;
                if (calTotalHeight > pagePixelHeight) {
                    pageRowIndexList.add(new RowPageIndex(r));
                    calTotalHeight = fixedHeight + calHeight;
                } else {
                    RowPropList subRowHeightList = rowSubModel.getRowPropList();
                    for (int k = 0, count = rowSubModel.getRowCount(); k < count; k++) {
                        if (rowSubModel.isFixedRow(k)) {
                            continue;
                        }

                        calTotalHeight += subRowHeightList.getHeight(k);
                        if (calTotalHeight > pagePixelHeight) {
                            pageRowIndexList.add(new RowPageIndex(r));
                            calTotalHeight = fixedHeight + rowSubFixedHeight;
                        }
                    }
                }

            } else {
                pageRowIndexList.add(new RowPageIndex(r));
                calTotalHeight = fixedHeight + calHeight;
            }
        }

        pageRowIndexList.add(new RowPageIndex(rowCount));
    }

    // 计算分页行位置
    private void calcRowPaging(int pagePixelHeight, int rowCount, int pageColumn) {
        RowPropList rowHeightList = this.model.getRowPropList();
        pageRowIndexList.add(new RowPageIndex(0));

        int fixedHeight = this.getFixedHeight(this.model);
        int calTotalHeight = fixedHeight;

        int calHeight = 0;

        String lcValue = "";
        String lccValue = "";
        for (int r = detailRows.getFrom(); r < detailRows.getTo(); r++) {
            if (this.model.isFixedRow(r)) {
                continue;
            }

            calTotalHeight += (calHeight = rowHeightList.getHeight(r));

            lccValue = StringUtils.convertToString(this.model.getCell(pageColumn, r).getValue(), "");
            if (lccValue.equals(lcValue)) {
                if (!rowHeightList.isGroup(r) && (calTotalHeight <= pagePixelHeight)) {
                    continue;
                }
            }
            // // TODO 支持子表换行
            // if (isRowHasSubReport(r)) {
            // ReportModel rowSubModel = this.getsRowSubReport(r);
            // int rowSubFixedHeight = this.getFixedHeight(rowSubModel);
            //
            // calTotalHeight -= calHeight;
            // calTotalHeight += rowSubFixedHeight;
            // if (calTotalHeight > pagePixelHeight) {
            // pageRowIndexList.add(new RowPageIndex(r, 0));
            // calTotalHeight = fixedHeight + calHeight;
            // } else {
            // RowPropList subRowHeightList = rowSubModel.getRowPropList();
            // for (int k = 0, count = rowSubModel.getRowCount(); k < count; k++) {
            // if (rowSubModel.isFixedRow(k)) {
            // continue;
            // }
            //
            // calTotalHeight += subRowHeightList.getHeight(k);
            // if (calTotalHeight > pagePixelHeight) {
            // pageRowIndexList.add(new RowPageIndex(r, k));
            // calTotalHeight = fixedHeight + rowSubFixedHeight;
            // }
            // }
            // }
            //
            // } else {

            if (!"".equals(lcValue)) {
                pageRowIndexList.add(new RowPageIndex(r));
            }

            lcValue = lccValue;
            calTotalHeight = fixedHeight + calHeight;
            // }
        }

        pageRowIndexList.add(new RowPageIndex(rowCount));
    }

    /**
     * @param row 所在行
     * @return 如果所在行包括子报表则返回true
     */
    private boolean isRowHasSubReport(int row) {
        for (int i = 0, count = this.model.getColumnCount(); i < count; i++) {
            Cell cell = this.model.getCell(i, row);
            Object value = cell.getValue();
            if ((value != null) && (value instanceof ReportModel)) {
                return true;
            }

        }
        return false;
    }

    /**
     * 子报表
     * 
     * @param row 所在行
     * @return 如果所在行包括子报表的子报表
     */
    private ReportModel getsRowSubReport(int row) {
        for (int i = 0, count = this.model.getColumnCount(); i < count; i++) {
            Cell cell = this.model.getCell(i, row);
            Object value = cell.getValue();
            if ((value != null) && (value instanceof ReportModel)) {
                return (ReportModel) value;
            }

        }
        return null;
    }

    /**
     * 返回模型固定行高度
     * 
     * @param reportModel 报表模型
     * @return 高度
     */
    private int getFixedHeight(ReportModel reportModel) {
        int result = 0;

        RowPropList rhL = reportModel.getRowPropList();

        for (int i : reportModel.getFixedHeaderRows()) {
            result += rhL.getHeight(i);
        }

        for (int i : reportModel.getFixedFooterRows()) {
            result += rhL.getHeight(i);
        }

        return result;
    }
}